/*   The MIT License
 *   
 *   Carina Engine
 *   Copyright (c) 2009 2010 2011 2012 Zdravko Velinov
 *   
 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 *   of this software and associated documentation files (the "Software"), to deal
 *   in the Software without restriction, including without limitation the rights
 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *   copies of the Software, and to permit persons to whom the Software is
 *   furnished to do so, subject to the following conditions:
 *
 *   The above copyright notice and this permission notice shall be included in
 *   all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *   THE SOFTWARE.
 */

#include "carina/gl-window-impl.hh"
#include "carina/common/exception.hh"
#include "carina/gl-renderer.hh"

#include <climits>

namespace Carina
{
GLWindowImpl::GLWindowImpl(GLLibrary& lib)
    :   m_GL(lib)
{

}

void GLWindowImpl::swapBuffers()
{
    glXSwapBuffers(m_Display, m_Window);
    XFlush(m_Display);
}

void GLWindowImpl::destroy()
{
    glXMakeCurrent(m_Display, 0, 0);
    glXDestroyContext(m_Display, m_GLXCtx);
    XFreeColormap(m_Display, m_XColormap);
}

void GLWindowImpl::resize(size_t w, size_t h)
{

}

void GLWindowImpl::create(WindowManager& wmgr, size_t w, size_t h, size_t samples, const WindowInfo& parent)
{
    int vi_attr_list[] =
    {
        GLX_X_RENDERABLE,  True,
        GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
        GLX_RENDER_TYPE,   GLX_RGBA_BIT,
        GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
        GLX_RED_SIZE,      8,
        GLX_GREEN_SIZE,    8,
        GLX_BLUE_SIZE ,    8,
        GLX_ALPHA_SIZE,    8,
        GLX_DEPTH_SIZE,    24,
        GLX_STENCIL_SIZE,  8,
        GLX_DOUBLEBUFFER,  True,
        GLX_SAMPLES,       (int)samples,
        None
    };

    WindowId p = parent.getWindowId();
    if(!p)
    {
        m_Display = wmgr.getDisplay();
        p = RootWindow(m_Display, DefaultScreen(m_Display));
    }
    else
        m_Display = parent.getDisplayId();

    if(!m_GL.initGLWindowSystem(WindowInfo(m_Window, m_Display)))
        THROW_EXCEPTION("the application has failed to load OpenGL's windowing system");

    int glx_major, glx_minor;
    if(!glXQueryVersion(m_Display, &glx_major, &glx_minor) || (glx_major == 1 && glx_minor < 3) || glx_major < 1)
        THROW_EXCEPTION("GLX version lower than 1.3 -- please check your graphics driver");

    int fbcount;
    auto fbc = CREATE_SCOPED(GLXFBConfig*, XFree);
    fbc = glXChooseFBConfig(m_Display, DefaultScreen(m_Display), vi_attr_list, &fbcount);
    if(!fbc)
        THROW_EXCEPTION("the application has failed to retrieve a framebuffer config");

    auto vi = CREATE_SCOPED(XVisualInfo*, XFree);
    vi = glXGetVisualFromFBConfig(m_Display, *fbc); 
    CE_ASSERT(vi, "Cannot extract visual information from framebuffer config");

    XSetWindowAttributes swa;
    swa.event_mask = event_mask;
    swa.colormap = m_XColormap = XCreateColormap(m_Display, p, vi->visual, AllocNone);
    XInstallColormap(m_Display, m_XColormap);

    m_Window = XCreateWindow(m_Display, p,
                             0, 0, w, h, 0, vi->depth, InputOutput,
                             vi->visual, CWColormap | CWEventMask, &swa);
    if(!m_Window)
        THROW_EXCEPTION("the application has failed to create a X11 window");

    if(glXCreateContextAttribsARB)
    {
        int ctx_attr_list[] =
        {
            GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
            GLX_CONTEXT_MINOR_VERSION_ARB, 0,
            None
        };

        m_GLXCtx = glXCreateContextAttribsARB(m_Display, *fbc, 0, True, ctx_attr_list);
        if(!m_GLXCtx)
            THROW_EXCEPTION("the application has failed to initialize an OpenGL 3.0 compatible GLX rendering context");
    }
    else
    {
        m_GLXCtx = glXCreateNewContext(m_Display, *fbc, GLX_RGBA_TYPE, 0, True);
        XSync(m_Display, False);
        if(!m_GLXCtx)
            THROW_EXCEPTION("the application has failed to initialize an OpenGL GLX rendering context");
    }

    glXMakeCurrent(m_Display, m_Window, m_GLXCtx);
    if(!m_GL.initGL())
        THROW_EXCEPTION("the application has failed to load OpenGL");

    m_Renderer = make_aligned_shared<GLRenderer>(*this);
}
}
